home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkShare.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-31  |  11.8 KB  |  410 lines

  1. /* 
  2.  * tkShare.c --
  3.  *
  4.  *    This module implements a simple mechanism for sharing
  5.  *    mouse- and button-related events among collections of
  6.  *    windows.  It is used primarily for menus.  For example,
  7.  *    if one menu is posted and mouse moves over the menu button
  8.  *    for a different menu, then the menubutton needs to see the
  9.  *    event so that it can post itself and unpost the first menu.
  10.  *
  11.  * Copyright 1990-1992 Regents of the University of California
  12.  * Permission to use, copy, modify, and distribute this
  13.  * software and its documentation for any purpose and without
  14.  * fee is hereby granted, provided that the above copyright
  15.  * notice appear in all copies.  The University of California
  16.  * makes no representations about the suitability of this
  17.  * software for any purpose.  It is provided "as is" without
  18.  * express or implied warranty.
  19.  */
  20.  
  21. #ifndef lint
  22. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkShare.c,v 1.10 92/05/31 16:20:12 ouster Exp $ SPRITE (Berkeley)";
  23. #endif /* not lint */
  24.  
  25. #include "tkConfig.h"
  26. #include "tk.h"
  27.  
  28. /*
  29.  * FILTHY HACK: the global variable below is used to tell TkPointerEvent
  30.  * not to do any processing on an event that we're forwarding from one
  31.  * window to another.  This is really ugly.  Eventually this file and
  32.  * tkGrab.c need to get merged together to produce something cleaner.
  33.  */
  34.  
  35. XEvent *tkShareEventPtr = NULL;
  36.  
  37. /*
  38.  * Sharing is implemented in terms of groups of windows, where events
  39.  * are shared among all the windows in a group.  One of the following
  40.  * structures exists for each group.
  41.  */
  42.  
  43. typedef struct Group {
  44.     Tk_Uid groupId;            /* Identifies group uniquely among all
  45.                      * share groups. */
  46.     Tk_Window *windows;            /* Pointer to array of windows in
  47.                      * this group.  Malloc'ed. */
  48.     int numWindows;            /* Number of windows currently in
  49.                      * this group. */
  50.     Tk_Window lastWindow;        /* Last window found that contained
  51.                      * an event.  Needed in order to
  52.                      * notify window when mouse moves out
  53.                      * of it.  NULL means nobody to
  54.                      * notify. */
  55.     XEvent *activeEvent;        /* If non-NULL, means that a recursive
  56.                      * call to Tk_HandleEvent is in
  57.                      * progress for this share group, and
  58.                      * identifies event.  NULL means no
  59.                      * recursive call in progress.  Used
  60.                      * to avoid infinite recursion. */
  61.     struct Group *nextPtr;        /* Next in list of all share groups. */
  62. } Group;
  63.  
  64. static Group *groupList = NULL;        /* First in list of all share groups
  65.                      * currently defined. */
  66.  
  67. /*
  68.  * Forward declarations for procedures defined later in this file:
  69.  */
  70.  
  71. static void        DeleteGroup _ANSI_ARGS_((Group *groupPtr));
  72. static void        ShareEventProc _ANSI_ARGS_((ClientData clientData,
  73.                 XEvent *eventPtr));
  74.  
  75. /*
  76.  *----------------------------------------------------------------------
  77.  *
  78.  * Tk_ShareEvents --
  79.  *
  80.  *    Add tkwin to a group of windows sharing events.
  81.  *
  82.  * Results:
  83.  *    None.
  84.  *
  85.  * Side effects:
  86.  *    In the future, if a button- or mouse-related event occurs for
  87.  *    any window in the same group as tkwin, but the mouse is actually
  88.  *    in tkwin (the event went to a different window because of a
  89.  *    grab) then a synthetic event will be generated with tkwin as
  90.  *    window and adjusted coordinates.
  91.  *
  92.  *----------------------------------------------------------------------
  93.  */
  94.  
  95. void
  96. Tk_ShareEvents(tkwin, groupId)
  97.     Tk_Window tkwin;            /* Token for window. */
  98.     Tk_Uid groupId;            /* Identifier for group among which
  99.                      * events are to be shared. */
  100. {
  101.     register Group *groupPtr;
  102.  
  103.     /*
  104.      * See if this group exists.  If so, add the window to the group.
  105.      */
  106.  
  107.     for (groupPtr = groupList; groupPtr != NULL;
  108.         groupPtr = groupPtr->nextPtr) {
  109.     Tk_Window *new;
  110.  
  111.     if (groupPtr->groupId != groupId) {
  112.         continue;
  113.     }
  114.     new = (Tk_Window *) ckalloc((unsigned)
  115.         (groupPtr->numWindows+1) * sizeof(Tk_Window *));
  116.     memcpy((VOID *) (new+1), (VOID *) groupPtr->windows, 
  117.         (groupPtr->numWindows * sizeof(Tk_Window *)));
  118.     ckfree((char *) groupPtr->windows);
  119.     groupPtr->windows = new;
  120.     groupPtr->windows[0] = tkwin;
  121.     groupPtr->numWindows++;
  122.     break;
  123.     }
  124.  
  125.     if (groupPtr == NULL) {
  126.     /*
  127.      * Group doesn't exist.  Make a new one.
  128.      */
  129.     
  130.     groupPtr = (Group *) ckalloc(sizeof(Group));
  131.     groupPtr->groupId = groupId;
  132.     groupPtr->windows = (Tk_Window *) ckalloc(sizeof (Tk_Window *));
  133.     groupPtr->windows[0] = tkwin;
  134.     groupPtr->numWindows = 1;
  135.     groupPtr->lastWindow = NULL;
  136.     groupPtr->activeEvent = NULL;
  137.     groupPtr->nextPtr = groupList;
  138.     groupList = groupPtr;
  139.     }
  140.  
  141.     /*
  142.      * Create an event handler so we find out about relevant events
  143.      * that are directed to tkwin.
  144.      */
  145.  
  146.     Tk_CreateEventHandler(tkwin,
  147.         ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
  148.         ShareEventProc, (ClientData) groupPtr);
  149. }
  150.  
  151. /*
  152.  *----------------------------------------------------------------------
  153.  *
  154.  * Tk_UnshareEvents --
  155.  *
  156.  *    Remove tkwin from a group of windows sharing events.
  157.  *
  158.  * Results:
  159.  *    None.
  160.  *
  161.  * Side effects:
  162.  *    Tkwin will no longer participate in event-sharing for the
  163.  *    given group, either as source of events or as destination.
  164.  *
  165.  *----------------------------------------------------------------------
  166.  */
  167.  
  168. void
  169. Tk_UnshareEvents(tkwin, groupId)
  170.     Tk_Window tkwin;            /* Token for window. */
  171.     Tk_Uid groupId;            /* Identifier for group. */
  172. {
  173.     register Group *groupPtr;
  174.     int i;
  175.  
  176.     for (groupPtr = groupList; groupPtr != NULL;
  177.         groupPtr = groupPtr->nextPtr) {
  178.     if (groupPtr->groupId != groupId) {
  179.         continue;
  180.     }
  181.     if (groupPtr->lastWindow == tkwin) {
  182.         groupPtr->lastWindow = NULL;
  183.     }
  184.     for (i = 0; i < groupPtr->numWindows; i++) {
  185.         if (groupPtr->windows[i] != tkwin) {
  186.         continue;
  187.         }
  188.         if ((i+1) < groupPtr->numWindows) {
  189.         memcpy((VOID *) (groupPtr->windows + i),
  190.             (VOID *) (groupPtr->windows + i + 1),
  191.             (groupPtr->numWindows - (i+1))*sizeof(Tk_Window *));
  192.         }
  193.         groupPtr->numWindows--;
  194.         Tk_DeleteEventHandler(tkwin,
  195.             ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
  196.             ShareEventProc, (ClientData) groupPtr);
  197.         if (groupPtr->numWindows == 0) {
  198.         DeleteGroup(groupPtr);
  199.         }
  200.         return;
  201.     }
  202.     }
  203. }
  204.  
  205. /*
  206.  *----------------------------------------------------------------------
  207.  *
  208.  * DeleteGroup --
  209.  *
  210.  *    This procedure is called when a group has no more members.
  211.  *    It deletes the group from the list of existing groups.
  212.  *
  213.  * Results:
  214.  *    None.
  215.  *
  216.  * Side effects:
  217.  *    Memory gets freed.
  218.  *
  219.  *----------------------------------------------------------------------
  220.  */
  221.  
  222. static void
  223. DeleteGroup(groupPtr)
  224.     Group *groupPtr;            /* Group to delete. */
  225. {
  226.     if (groupList == groupPtr) {
  227.     groupList = groupPtr->nextPtr;
  228.     } else {
  229.     register Group *prevPtr;
  230.  
  231.     for (prevPtr = groupList; ; prevPtr = prevPtr->nextPtr) {
  232.         if (prevPtr == NULL) {
  233.         panic("DeleteGroup couldn't find group on shareList");
  234.         }
  235.         if (prevPtr->nextPtr == groupPtr) {
  236.         prevPtr->nextPtr = groupPtr->nextPtr;
  237.         break;
  238.         }
  239.     }
  240.     }
  241.     ckfree((char *) groupPtr->windows);
  242.     ckfree((char *) groupPtr);
  243. }
  244.  
  245. /*
  246.  *----------------------------------------------------------------------
  247.  *
  248.  * ShareEventProc --
  249.  *
  250.  *    This procedure is invoked by the Tk dispatcher when an event
  251.  *    occurs for which we need to implement sharing.
  252.  *
  253.  * Results:
  254.  *    None.
  255.  *
  256.  * Side effects:
  257.  *    If the mouse is actually in a window other than the one for
  258.  *    which the event occurred, generate a new event translated to
  259.  *    that window.
  260.  *
  261.  *----------------------------------------------------------------------
  262.  */
  263.  
  264. static void
  265. ShareEventProc(clientData, eventPtr)
  266.     ClientData clientData;        /* Information about share group. */
  267.     register XEvent *eventPtr;        /* Event that just occurred. */
  268. {
  269.     register Group *groupPtr = (Group *) clientData;
  270.     register Tk_Window tkwin;
  271.     Window window;
  272.     XEvent newEvent, *savedActive, *savedShareEventPtr;
  273.     int i, x, y;
  274.     Tk_Uid savedId;
  275.     register Group *grpPtr;
  276.  
  277.     /*
  278.      * If this event was a synthetic one that we generated, then
  279.      * don't bother to process it again.
  280.      */
  281.  
  282.     if (groupPtr->activeEvent == eventPtr) {
  283.     return;
  284.     }
  285.     savedActive = groupPtr->activeEvent;
  286.     groupPtr->activeEvent = &newEvent;
  287.     savedId = groupPtr->groupId;
  288.  
  289.     savedShareEventPtr = tkShareEventPtr;
  290.     tkShareEventPtr = &newEvent;
  291.  
  292.     /*
  293.      * Scan through all of the windows for this group to find the
  294.      * first one (if any) that contains the event.
  295.      */
  296.  
  297.     tkwin = NULL;        /* Not needed, but stops compiler warning. */
  298.     for (i = 0; i < groupPtr->numWindows; i++) {
  299.     Tk_Window tkwin2;
  300.  
  301.     tkwin = groupPtr->windows[i];
  302.     Tk_GetRootCoords(tkwin, &x, &y);
  303.     x = eventPtr->xmotion.x_root - x - Tk_Changes(tkwin)->border_width;
  304.     y = eventPtr->xmotion.y_root - y - Tk_Changes(tkwin)->border_width;
  305.     if ((x < 0) || (y < 0) || (x >= Tk_Width(tkwin))
  306.         || (y >= Tk_Height(tkwin))) {
  307.         continue;
  308.     }
  309.     for (tkwin2 = tkwin; ; tkwin2 = Tk_Parent(tkwin2)) {
  310.         if (tkwin2 == NULL) {
  311.         goto foundWindow;
  312.         }
  313.         if (!Tk_IsMapped(tkwin2)) {
  314.         break;
  315.         }
  316.         if (((Tk_FakeWin *) (tkwin2))->flags & TK_TOP_LEVEL) {
  317.         goto foundWindow;
  318.         }
  319.     }
  320.     }
  321.  
  322.     foundWindow:
  323.     window = None;    /* Not really needed but stops compiler warning. */
  324.     if (i >= groupPtr->numWindows) {
  325.     tkwin = NULL;
  326.     } else {
  327.     window = Tk_WindowId(tkwin);
  328.     }
  329.  
  330.     /*
  331.      * SPECIAL NOTE:  it is possible that any or all of the information
  332.      * in groupPtr could be modified as part of the processing of the
  333.      * events that we generate and hand to Tk_HandleEvent below.  For this
  334.      * to work smoothly, it is imperative that we extract any information
  335.      * we need from groupPtr (and from tkwin's, since they could be
  336.      * deleted) before the first call to Tk_HandleEvent below.  The code
  337.      * below may potentially pass an X window identifier to Tk_HandleEvent
  338.      * after the window has been deleted, but as long as identifiers
  339.      * aren't recycled Tk_HandleEvent will simply discard the event if
  340.      * this occurs.
  341.      */
  342.  
  343.     /*
  344.      * If the pointer is in a different window now than the last time
  345.      * we were invoked, send a LeaveNotify event to the old window and
  346.      * an EnterNotify event to the new window.
  347.      */
  348.  
  349.     newEvent = *eventPtr;
  350.     newEvent.xany.send_event = True;
  351.     if (tkwin != groupPtr->lastWindow) {
  352.     newEvent = *eventPtr;
  353.     newEvent.xany.send_event = True;
  354.     newEvent.xcrossing.mode = TK_NOTIFY_SHARE;
  355.     newEvent.xcrossing.detail = NotifyAncestor;
  356.     newEvent.xcrossing.same_screen = True;
  357.     newEvent.xcrossing.state = eventPtr->xmotion.state;
  358.     if (groupPtr->lastWindow != NULL) {
  359.         newEvent.xcrossing.type = LeaveNotify;
  360.         newEvent.xcrossing.window = Tk_WindowId(groupPtr->lastWindow);
  361.         Tk_GetRootCoords(groupPtr->lastWindow, &newEvent.xcrossing.x,
  362.             &newEvent.xcrossing.y);
  363.         newEvent.xcrossing.x = eventPtr->xmotion.x_root
  364.             - newEvent.xcrossing.x
  365.             - Tk_Changes(groupPtr->lastWindow)->border_width;
  366.         newEvent.xcrossing.y = eventPtr->xmotion.y_root
  367.             - newEvent.xcrossing.y
  368.             - Tk_Changes(groupPtr->lastWindow)->border_width;
  369.         Tk_HandleEvent(&newEvent);
  370.     }
  371.     if (tkwin != NULL) {
  372.         newEvent.xcrossing.type = EnterNotify;
  373.         newEvent.xcrossing.window = window;
  374.         newEvent.xcrossing.x = x;
  375.         newEvent.xcrossing.y = y;
  376.         Tk_HandleEvent(&newEvent);
  377.     }
  378.     groupPtr->lastWindow = tkwin;
  379.     }
  380.  
  381.     /*
  382.      * If the pointer is in the window to which the event was sent,
  383.      * then we needn't do any forwarding at all.  Ditto if the pointer
  384.      * isn't in any window at all.
  385.      */
  386.  
  387.     if ((tkwin != NULL) && (Tk_WindowId(tkwin) != eventPtr->xmotion.window)) {
  388.     newEvent = *eventPtr;
  389.     newEvent.xmotion.send_event = True;
  390.     newEvent.xmotion.window = window;
  391.     newEvent.xmotion.x = x;
  392.     newEvent.xmotion.y = y;
  393.     Tk_HandleEvent(&newEvent);
  394.     }
  395.  
  396.     /*
  397.      * Only restore the activeEvent if the group still exists.
  398.      * (It could be deleted as a side effect of processing the event.)
  399.      */
  400.  
  401.     for (grpPtr = groupList; grpPtr != NULL; grpPtr = grpPtr->nextPtr) {
  402.     if (grpPtr->groupId == savedId) {
  403.         groupPtr->activeEvent = savedActive;
  404.         break;
  405.     }
  406.     }
  407.  
  408.     tkShareEventPtr = savedShareEventPtr;
  409. }
  410.